/*
 * @描述: 
 * @创建者: kilomChou
 * @Date: 2021-04-11 16:40:42
 * @最后编辑: kilomChou
 * @LastEditTime: 2021-05-20 11:32:32
 */
import { VWorld } from "../../data/XlsxVo";
import { RandUtil } from "../../util/RandUtil";
import { Util } from "../../util/Util";
import { ICircle, IPos, IRect, ISize, Vec2 } from "../../core/vec2/Vec2";
import Snake from "../snake/Snake";
import SnakeNode from "../snake/SnakeNode";
import Chunk from "./Chunk";
import Food from "../food/Food";
import Vec2Operator from "../../core/vec2/Vec2Operator";
import WorldFoodMgr from "./manager/WorldFoodMgr";
import WorldSnakeMgr from "./manager/WorldSnakeMgr";
import GameGlobal from "../GameGlobal";
import WorldPlayerMgr from "./manager/WorldPlayerMgr";
import WorldAISnakeMgr from "./manager/WorldAISnakeMgr";
const [util, ru, v2, v2op] = [Util, RandUtil, Vec2, new Vec2Operator()];

/** 游戏世界 */
export default class World {

    constructor(
        /** 游戏全局 */
        readonly global: GameGlobal) {

        //创建区块
        v2.sizeAs(tmpRect, this.chunkSize);
        for (let row = 0; row < this.chunkSideCount; row++) {
            this.chunkGrid[row] = [];
            for (let col = 0; col < this.chunkSideCount; col++) {
                v2.setPos(tmpRect,
                    (col * this.chunkSize.w + this.chunkRadius.w) - this.radius.w,
                    (row * this.chunkSize.h + this.chunkRadius.h) - this.radius.h);
                let chunk = new Chunk(this, tmpRect);
                this.chunks.push(chunk);
                this.chunkGrid[row][col] = chunk;
            }
        }
    }

    /** 世界宽高 */
    readonly size: ISize = { w: VWorld.cfg.width, h: VWorld.cfg.height };
    /** 世界形状 */
    readonly rect: IRect = { x: 0, y: 0, ...this.size };
    /** 世界半径 */
    readonly radius: ISize = { w: this.size.w * 0.5, h: this.size.h * 0.5 };
    /** 区块行列数量 */
    readonly chunkSideCount: number = Math.floor(Math.max(this.size.w, this.size.h) / VWorld.cfg.chunkSize);
    /** 区块宽高 */
    readonly chunkSize: ISize = { w: this.size.w / this.chunkSideCount, h: this.size.h / this.chunkSideCount };
    /** 区块半径 */
    readonly chunkRadius: ISize = { w: this.chunkSize.w * 0.5, h: this.chunkSize.h * 0.5 };
    /** 区块数组 */
    readonly chunks: Chunk[] = [];
    /** 区块网格 */
    readonly chunkGrid: Chunk[][] = [];

    /** 蛇管理器 */
    readonly snakeMgr = new WorldSnakeMgr(this);
    /** AI蛇管理器 */
    readonly aiSnakeMgr = new WorldAISnakeMgr(this);
    /** 食物管理器 */
    readonly foodMgr = new WorldFoodMgr(this);
    /** 玩家管理器 */
    readonly playerMgr = new WorldPlayerMgr(this);

    /** 帧间隔时间 单位s */
    get dt() { return this.global.dt; }
    /** 事件派发器 */
    get ed() { return this.global.ed; }
    /** 定时器 */
    get timer() { return this.global.timer; }
    /** 玩家id映射 */
    get playerMap() { return this.playerMgr.playerMap; }

    /** 初始化 */
    start() {
        this.snakeMgr.start();
        this.aiSnakeMgr.start();
        this.foodMgr.start();
        this.playerMgr.start();
    }

    /** 每帧更新 */
    update() {
        this.playerMgr.update();
        this.foodMgr.update();
        this.snakeMgr.update();
        this.aiSnakeMgr.update();
    }

    /** 获取出生点 */
    getSpawnPos<T extends IPos>(out: T = tmpPosSpawnPos as T) {
        out.x = ru.rand(-this.radius.w, this.radius.w);
        out.y = ru.rand(-this.radius.h, this.radius.h);
        return out;
    }

    /** 获取坐标点所在的区块 */
    getChunk(pos: IPos) {
        let x = pos.x + this.radius.w;
        let y = util.range(pos.y + this.radius.h, 0, this.size.h);
        let max = this.chunkSideCount - 1;
        let col = util.range(Math.floor(x / this.chunkSize.w), 0, max);
        let row = util.range(Math.floor(y / this.chunkSize.h), 0, max);
        return this.chunkGrid[row][col];
    }

    /** 获取物体所覆盖的区块 */
    getChunks(obj: ICircle | IRect, isCir = true) {
        let chunk = this.getChunk(obj);
        let chunks = [chunk];
        v2op.setObj(tmpRect);
        if (isCir) {
            //为了方便将圆形视作正方形
            let w = (obj as ICircle).r * 2;
            v2op.setSize(w, w);
        } else {
            v2op.sizeAs(obj as IRect);
        }
        v2op.setPos(
            obj.x - tmpRect.w * 0.5 + this.radius.w,
            obj.y - tmpRect.h * 0.5 + this.radius.h
        );

        let max = this.chunkSideCount - 1;
        let col1 = util.range(Math.floor(tmpRect.x / this.chunkSize.w), 0, max);
        let row1 = util.range(Math.floor(tmpRect.y / this.chunkSize.h), 0, max);
        v2op.oPos(tmpRect.w, tmpRect.h);
        let col2 = util.range(Math.floor(tmpRect.x / this.chunkSize.w), 0, max);
        let row2 = util.range(Math.floor(tmpRect.y / this.chunkSize.h), 0, max);

        for (let row = row1; row <= row2; row++) {
            for (let col = col1; col <= col2; col++) {
                let coverChunk = this.chunkGrid[row][col];
                if (coverChunk == chunk) continue;
                chunks.push(coverChunk);
            }
        }
        return chunks;
    }
}

const tmpRect: IRect = { x: 0, y: 0, w: 0, h: 0 };
/** getSpawnPos()专用 */
const tmpPosSpawnPos: IPos = { x: 0, y: 0 };